/* Copyright (c) 2008,                                                                  vided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notices,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name Visigoth Software Society, FreeCC, Sun Microsystems, Inc.       
 *     
 *     
 *    
 *       nor the names of any other contributors may be used to endorse 
 *       or promote products derived from this software without specific prior written 
 *       permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS"
p* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF     
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.visigoths.freecc;

import java.io.IOException;
import java.util.*;
import java.text.*;


public class FreeCCOptions implements Cloneable {
    
    static Set<String> optionsUsedFromInclude = new HashSet<String>();
    
    static {
        optionsUsedFromInclude.add("NODE_PACKAGE");
        optionsUsedFromInclude.add("SMART_NODE_CREATION");
        optionsUsedFromInclude.add("NODE_DEFAULT_VOID");
        optionsUsedFromInclude.add("NODE_PREFIX");
    }


    private Grammar grammar;
    
    public FreeCCOptions(String[] args) throws IOException {
        init();
        String filename = args[args.length -1];
        if (isOption(filename)) {
            throw new IOException("Last argument \"" + filename + " is not a filename.");
        }
        for (int i=0; i<args.length -1; i++) {
            String arg = args[i];
            if (!isOption(arg)) {
                throw new IOException("Argument \"" + arg + "\" must be an option setting.");
            }
            setCmdLineOption(arg);
        }
    }
    
    public FreeCCOptions(Grammar grammar) {
        this.grammar = grammar;
        init();
    }

    void setGrammar(Grammar grammar) {
        this.grammar = grammar;
    }
    
    public FreeCCOptions clone() {
        FreeCCOptions clone = null;
        try {
            clone = (FreeCCOptions) super.clone();
        } 
        catch (CloneNotSupportedException cnse) {
        }
        clone.optionValues = new HashMap<String, Object>(optionValues);
        clone.cmdLineSetting = new HashSet<String>(cmdLineSetting);
        clone.inputFileSetting = new HashSet<String>(inputFileSetting);
        return clone;
    }

    /**
     * A mapping of option names (Strings) to values (Integer, Boolean, String).
     * This table is initialized by the main program. Its contents defines the
     * set of legal options. Its initial values define the default option
     * values, and the option types can be determined from these values too.
     */
    protected Map<String, Object> optionValues = new HashMap<String, Object>();

    /**
     * Convenience method to retrieve integer options.
     */
    protected int intValue(final String option) {
        return ((Integer) optionValues.get(option)).intValue();
    }

    /**
     * Convenience method to retrieve boolean options.
     */
    protected boolean booleanValue(final String option) {
        return ((Boolean) optionValues.get(option)).booleanValue();
    }

    /**
     * Convenience method to retrieve string options.
     */
    protected String stringValue(final String option) {
        return (String) optionValues.get(option);
    }

    /**
     * Keep track of what options were set as a command line argument. We use
     * this to see if the options set from the command line and the ones set in
     * the input files clash in any way.
     */
    private Set<String> cmdLineSetting = new HashSet<String>();

    /**
     * Keep track of what options were set from the grammar file. We use this to
     * see if the options set from the command line and the ones set in the
     * input files clash in any way.
     */
    private Set<String> inputFileSetting = new HashSet<String>();

    /**
     * Initialize for JavaCC
     */
    public void init() {
        optionValues = new HashMap<String, Object>();
        optionValues.put("LOOKAHEAD", 1);
        optionValues.put("CHOICE_AMBIGUITY_CHECK", 2);
        optionValues.put("OTHER_AMBIGUITY_CHECK", 1);

        optionValues.put("DEBUG_PARSER", false);
        optionValues.put("DEBUG_LOOKAHEAD", false);
        optionValues.put("DEBUG_TOKEN_MANAGER", false);
        optionValues.put("ERROR_REPORTING", true);
        optionValues.put("JAVA_UNICODE_ESCAPE", false);
        optionValues.put("IGNORE_CASE", false);
        optionValues.put("USER_TOKEN_MANAGER", false);
        optionValues.put("USER_CHAR_STREAM", false);
        optionValues.put("BUILD_PARSER", true);
        optionValues.put("BUILD_TOKEN_MANAGER", true);
        optionValues.put("TOKEN_MANAGER_USES_PARSER", false);
        optionValues.put("SANITY_CHECK", true);
        optionValues.put("FORCE_LA_CHECK", false);
        
        optionValues.put("PARSER_PACKAGE", "");

        optionValues.put("OUTPUT_DIRECTORY", "");
        optionValues.put("JDK_VERSION", "1.4");
        optionValues.put("TOKEN_EXTENDS", "");
        optionValues.put("TOKEN_FACTORY", "");
        optionValues.put("UNICODE_INPUT", true);
        optionValues.put("CACHE_TOKENS", false);
        optionValues.put("CSS", "");

        optionValues.put("MULTI", false);
        optionValues.put("NODE_DEFAULT_VOID", false);
        optionValues.put("SMART_NODE_CREATION", false);
        optionValues.put("NODE_SCOPE_HOOK", false);
        optionValues.put("NODE_USES_PARSER", false);
        optionValues.put("VISITOR", false);
        optionValues.put("TRACK_TOKENS", false);

        optionValues.put("NODE_PREFIX", "AST");
        optionValues.put("NODE_EXTENDS", "");
        optionValues.put("NODE_CLASS", "");
        optionValues.put("NODE_FACTORY", false);
        optionValues.put("NODE_PACKAGE", "");
        optionValues.put("VISITOR_DATA_TYPE", "");
        optionValues.put("VISITOR_RETURN_TYPE", "Object");
        optionValues.put("VISITOR_EXCEPTION", "");
        optionValues.put("TREE_BUILDING_DEFAULT", true);
        optionValues.put("TREE_BUILDING_ENABLED", false);
        optionValues.put("TOKENS_ARE_NODES", false);
        optionValues.put("SPECIAL_TOKENS_ARE_NODES", false);
        optionValues.put("FREEMARKER_NODES", false);
        optionValues.put("BEAUTIFY_OUTPUT", true);
        optionValues.put("BASE_SRC_DIR", "");
    }

    /**
     * Determine if a given command line argument might be an option flag.
     * Command line options start with a dash&nbsp;(-).
     * 
     * @param opt
     *            The command line argument to examine.
     * @return True when the argument looks like an option flag.
     */
    static public boolean isOption(final String opt) {
        return opt != null && opt.length() > 1 && opt.charAt(0) == '-';
    }

    /**
     * Help function to handle cases where the meaning of an option has changed
     * over time. If the user has supplied an option in the old format, it will
     * be converted to the new format.
     * 
     * @param name
     *            The name of the option being checked.
     * @param value
     *            The option's value.
     * @return The upgraded value.
     */
    public Object upgradeValue(final String name, Object value) {
        if (name.equalsIgnoreCase("NODE_FACTORY") && value.getClass() == Boolean.class) {
            if (((Boolean) value).booleanValue()) {
                value = "*";
            } else {
                value = "";
            }
        }

        return value;
    }
    
    public void setInputFileOption(Object nameloc, Object valueloc, String name, Object value, boolean inInclude) {
        
        if (inInclude) {
            // For now, we just ignore any options if we are within a INCLUDE_GRAMMAR processing.
            // This will be revisited later.
            return;
        }
        
        String s = name.toUpperCase();

        
        if (s.equalsIgnoreCase("STATIC")) {
            FreeCCNode node = (nameloc instanceof FreeCCNode) ? (FreeCCNode) nameloc : null;
            grammar.addWarning(node, "In FreeCC, the STATIC option is superfluous. All parsers are non-static. Option setting will be ignored.");
            return;
        }
        else if (s.equalsIgnoreCase("KEEP_LINE_COLUMN")) {
            FreeCCNode node = (nameloc instanceof FreeCCNode) ? (FreeCCNode) nameloc : null;
            grammar.addWarning(node, "In FreeCC, the KEEP_LINE_COLUMN option is superfluous. Location info is always retained. Option setting will be ignored.");
            return;
        }
        else if (s.equalsIgnoreCase("COMMON_TOKEN_ACTION")) {
            FreeCCNode node = (nameloc instanceof FreeCCNode) ? (FreeCCNode) nameloc : null;
            grammar.addWarning(node, "In FreeCC, the COMMON_TOKEN_ACTION option is superfluous. If your TOKEN_MGR_DECLS contains a CommonTokenAction(Token t) method it is used. Option setting will be ignored.");
            return;
        }
        else if (!optionValues.containsKey(s)) {
            FreeCCNode node = (nameloc instanceof FreeCCNode) ? (FreeCCNode) nameloc : null;
            grammar.addWarning(node, "Bad option name \"" + name
                    + "\".  Option setting will be ignored.");
            return;
        }
        final Object existingValue = optionValues.get(s);

        if (existingValue != null) {
            if ((existingValue.getClass() != value.getClass())
                    || (value instanceof Integer && ((Integer) value).intValue() <= 0)) {
                FreeCCNode node = (valueloc instanceof FreeCCNode) ? (FreeCCNode) valueloc : null;
                grammar.addWarning(node, "Bad option value \"" + value + "\" for \"" + name
                        + "\".  Option setting will be ignored.");
                return;
            }

            if (inputFileSetting.contains(s)) {
                FreeCCNode node = (nameloc instanceof FreeCCNode) ? (FreeCCNode) nameloc : null;
                grammar.addWarning(node, "Duplicate option setting for \"" + name
                        + "\" will be ignored.");
                return;
            }

            if (cmdLineSetting.contains(s)) {
                if (!existingValue.equals(value)) {
                    FreeCCNode node = (valueloc instanceof FreeCCNode) ? (FreeCCNode) valueloc
                            : null;
                    grammar.addWarning(node, "Command line setting of \"" + name
                            + "\" modifies option value in file.");
                }
                return;
            }
        }

        optionValues.put(s, value);
        inputFileSetting.add(s);
    }

    /**
     * Process a single command-line option. The option is parsed and stored in
     * the optionValues map.
     * 
     * @param arg
     */
    public void setCmdLineOption(String arg) {
        final String s;
        if (arg.charAt(0) == '-') {
            s = arg.substring(1);
        } else {
            s = arg;
        }

        String name;
        Object Val;

        // Look for the first ":" or "=", which will separate the option name
        // from its value (if any).
        final int index1 = s.indexOf('=');
        final int index2 = s.indexOf(':');
        final int index;

        if (index1 < 0)
            index = index2;
        else if (index2 < 0)
            index = index1;
        else if (index1 < index2)
            index = index1;
        else
            index = index2;

        if (index < 0) {
            name = s.toUpperCase();
            if (optionValues.containsKey(name)) {
                Val = Boolean.TRUE;
            } else if (name.length() > 2 && name.charAt(0) == 'N' && name.charAt(1) == 'O') {
                Val = Boolean.FALSE;
                name = name.substring(2);
            } else {
                System.out.println("Warning: Bad option \"" + arg + "\" will be ignored.");
                return;
            }
        } else {
            name = s.substring(0, index).toUpperCase();
            if (s.substring(index + 1).equalsIgnoreCase("TRUE")) {
                Val = Boolean.TRUE;
            } else if (s.substring(index + 1).equalsIgnoreCase("FALSE")) {
                Val = Boolean.FALSE;
            } else {
                try {
                    int i = Integer.parseInt(s.substring(index + 1));
                    if (i <= 0) {
                        System.out.println("Warning: Bad option value in \"" + arg
                                + "\" will be ignored.");
                        return;
                    }
                    Val = new Integer(i);
                } catch (NumberFormatException e) {
                    Val = s.substring(index + 1);
                    if (s.length() > index + 2) {
                        // i.e., there is space for two '"'s in value
                        if (s.charAt(index + 1) == '"' && s.charAt(s.length() - 1) == '"') {
                            // remove the two '"'s.
                            Val = s.substring(index + 2, s.length() - 1);
                        }
                    }
                }
            }
        }

        if (!optionValues.containsKey(name)) {
            System.out.println("Warning: Bad option \"" + arg + "\" will be ignored.");
            return;
        }
        Object valOrig = optionValues.get(name);
        if (Val.getClass() != valOrig.getClass()) {
            System.out.println("Warning: Bad option value in \"" + arg + "\" will be ignored.");
            return;
        }
        if (cmdLineSetting.contains(name)) {
            System.out
                    .println("Warning: Duplicate option setting \"" + arg + "\" will be ignored.");
            return;
        }

        Val = upgradeValue(name, Val);

        optionValues.put(name, Val);
        cmdLineSetting.add(name);
    }

    public void normalize() {
        if (getDebugLookahead() && !getDebugParser()) {
            if (cmdLineSetting.contains("DEBUG_PARSER")
                    || inputFileSetting.contains("DEBUG_PARSER")) {
                grammar.addWarning(null, "True setting of option DEBUG_LOOKAHEAD overrides "
                        + "false setting of option DEBUG_PARSER.");
            }
            optionValues.put("DEBUG_PARSER", Boolean.TRUE);
        }
    }

    /**
     * Find the lookahead setting.
     * 
     * @return The requested lookahead value.
     */
    public int getLookahead() {
        return intValue("LOOKAHEAD");
    }

    /**
     * Find the choice ambiguity check value.
     * 
     * @return The requested choice ambiguity check value.
     */
    public int getChoiceAmbiguityCheck() {
        return intValue("CHOICE_AMBIGUITY_CHECK");
    }

    /**
     * Find the other ambiguity check value.
     * 
     * @return The requested other ambiguity check value.
     */
    public int getOtherAmbiguityCheck() {
        return intValue("OTHER_AMBIGUITY_CHECK");
    }

    /**
     * Find the debug parser value.
     * 
     * @return The requested debug parser value.
     */
    public boolean getDebugParser() {
        return booleanValue("DEBUG_PARSER");
    }

    /**
     * Find the debug lookahead value.
     * 
     * @return The requested debug lookahead value.
     */
    public boolean getDebugLookahead() {
        return booleanValue("DEBUG_LOOKAHEAD");
    }

    /**
     * Find the debug tokenmanager value.
     * 
     * @return The requested debug tokenmanager value.
     */
    public boolean getDebugTokenManager() {
        return booleanValue("DEBUG_TOKEN_MANAGER");
    }

    /**
     * Find the error reporting value.
     * 
     * @return The requested error reporting value.
     */
    public boolean getErrorReporting() {
        return booleanValue("ERROR_REPORTING");
    }

    /**
     * Find the Java unicode escape value.
     * 
     * @return The requested Java unicode escape value.
     */
    public boolean getJavaUnicodeEscape() {
        return booleanValue("JAVA_UNICODE_ESCAPE");
    }

    /**
     * Find the unicode input value.
     * 
     * @return The requested unicode input value.
     */
    public boolean getUnicodeInput() {
        return booleanValue("UNICODE_INPUT");
    }

    /**
     * Find the ignore case value.
     * 
     * @return The requested ignore case value.
     */
    public boolean getIgnoreCase() {
        return booleanValue("IGNORE_CASE");
    }

    /**
     * Find the user tokenmanager value.
     * 
     * @return The requested user tokenmanager value.
     */
    public boolean getUserTokenManager() {
        return booleanValue("USER_TOKEN_MANAGER");
    }

    /**
     * Find the user charstream value.
     * 
     * @return The requested user charstream value.
     */
    public boolean getUserCharStream() {
        return booleanValue("USER_CHAR_STREAM");
    }

    /**
     * Find the build parser value.
     * 
     * @return The requested build parser value.
     */
    public boolean getBuildParser() {
        return booleanValue("BUILD_PARSER");
    }

    /**
     * Find the build token manager value.
     * 
     * @return The requested build token manager value.
     */
    public boolean getBuildTokenManager() {
        return booleanValue("BUILD_TOKEN_MANAGER");
    }

    /**
     * Find the token manager uses parser value.
     * 
     * @return The requested token manager uses parser value;
     */
    public boolean getTokenManagerUsesParser() {
        return booleanValue("TOKEN_MANAGER_USES_PARSER");
    }

    /**
     * Find the sanity check value.
     * 
     * @return The requested sanity check value.
     */
    public boolean getSanityCheck() {
        return booleanValue("SANITY_CHECK");
    }

    /**
     * Find the force lookahead check value.
     * 
     * @return The requested force lookahead value.
     */
    public boolean getForceLaCheck() {
        return booleanValue("FORCE_LA_CHECK");
    }

    /**
     * Find the common token action value.
     * 
     * @return The requested common token action value.
     */

    public boolean getCommonTokenAction() {
        return commonTokenAction;
    }
    
    public void setCommonTokenAction(boolean commonTokenAction) {
        this.commonTokenAction = commonTokenAction;
    }
    
    private boolean commonTokenAction;

    /**
     * Find the cache tokens value.
     * 
     * @return The requested cache tokens value.
     */
    public boolean getCacheTokens() {
        return booleanValue("CACHE_TOKENS");
    }

    /**
     * @return a CSS file to use for outputting HTML docs
     */
    public String getCSS() {
        return stringValue("CSS");
    }

    /**
     * Determine if the output language is at least the specified version.
     * 
     * @param version
     *            the version to check against. E.g. <code>1.5</code>
     * @return true if the output version is at least the specified version.
     */
    public boolean jdkVersionAtLeast(double version) {
        double jdkVersion = Double.parseDouble(getJdkVersion());
        
        // Comparing doubles is safe here, as it is two simple assignments.
        return jdkVersion >= version;
    }

    /**
     * Return the Token's superclass.
     * 
     * @return The required base class for Token.
     */
    public String getTokenExtends() {
        return stringValue("TOKEN_EXTENDS");
    }

    /**
     * Return the Token's factory class.
     * 
     * @return The required factory class for Token.
     */
    public String getTokenFactory() {
        return stringValue("TOKEN_FACTORY");
    }
    
    public boolean getBeautifyOutput() {
        return booleanValue("BEAUTIFY_OUTPUT");
    }

    /**
     * Find the output directory.
     * 
     * @return The requested output directory.
     */
    public String getOutputDirectory() {
        return stringValue("OUTPUT_DIRECTORY");
    }

    public String stringBufOrBuild() {
        if (getJdkVersion().equals("1.5") || getJdkVersion().equals("1.6")) {
            return "StringBuilder";
        } else {
            return "StringBuffer";
        }
    }

    /**
     *  Check options for consistency
     */
    public void validate() {
      if (!getVisitor()) {
        if (getVisitorDataType().length() > 0) {
          grammar.addWarning(null, "VISITOR_DATA_TYPE option will be ignored since VISITOR is false");
        }
        if (getVisitorReturnType().length() > 0 && !getVisitorReturnType().equals("Object")) {
          grammar.addWarning(null, "VISITOR_RETURN_TYPE option will be ignored since VISITOR is false");
        }
        if (getVisitorException().length() > 0) {
          grammar.addWarning(null, "VISITOR_EXCEPTION option will be ignored since VISITOR is false");
        }
      }
    }


    /**
     * Find the JDK version.
     *
     * @return The specified JDK version.
     */
    public String getJdkVersion() {
        return stringValue("JDK_VERSION");
    }

    /**
     * Find the multi value.
     *
     * @return The requested multi value.
     */
    public boolean getMulti() {
        return booleanValue("MULTI");
    }

    /**
     * Find the node default void value.
     *
     * @return The requested node default void value.
     */
    public boolean getNodeDefaultVoid() {
        return booleanValue("NODE_DEFAULT_VOID");
    }
    
    public boolean getSmartNodeCreation() {
        return booleanValue("SMART_NODE_CREATION");
    }
    

    /**
     * Find the node scope hook value.
     *
     * @return The requested node scope hook value.
     */
    public boolean getNodeScopeHook() {
        return booleanValue("NODE_SCOPE_HOOK");
    }

    /**
     * Find the node factory value.
     *
     * @return The requested node factory value.
     */
    public boolean getNodeFactory() {
        return booleanValue("NODE_FACTORY");
    }

    /**
     * Find the node uses parser value.
     *
     * @return The requested node uses parser value.
     */
    public boolean getNodeUsesParser() {
        return booleanValue("NODE_USES_PARSER");
    }

    /**
     * Find the visitor value.
     *
     * @return The requested visitor value.
     */
    public boolean getVisitor() {
        return booleanValue("VISITOR");
    }

    /**
     * Find the trackTokens value.
     *
     * @return The requested trackTokens value.
     */
    public boolean getTrackTokens() {
        return booleanValue("TRACK_TOKENS");
    }

    /**
     * Find the node prefix value.
     *
     * @return The requested node prefix value.
     */
    public String getNodePrefix() {
        return stringValue("NODE_PREFIX");
    }

    /**
     * Find the node super class name.
     *
     * @return The requested node super class
     */
    public String getNodeExtends() {
        return stringValue("NODE_EXTENDS");
    }

    /**
     * Find the node class name.
     *
     * @return The requested node class
     */
    public String getNodeClass() {
        return stringValue("NODE_CLASS");
    }
    
    public String getNodePackage() {
        return stringValue("NODE_PACKAGE");
    }
    
    public String getParserPackage() {
        return stringValue("PARSER_PACKAGE");
    }

    /**
     * Find the visitor exception value
     *
     * @return The requested visitor exception value.
     */
    public String getVisitorException() {
        return stringValue("VISITOR_EXCEPTION");
    }

    /**
     * Find the visitor data type value
     *
     * @return The requested visitor data type value.
     */
    public String getVisitorDataType() {
        String type = stringValue("VISITOR_DATA_TYPE");
        if (type.equals("")) {
            type = "Object";
        }
        return type;
    }

    /**
     * Find the visitor return type value
     *
     * @return The requested visitor return type value.
     */
    public String getVisitorReturnType() {
        return stringValue("VISITOR_RETURN_TYPE");
    }

    public boolean getTreeBuildingEnabled() {
        return booleanValue("TREE_BUILDING_ENABLED") && getBuildParser();
    }
    
    public boolean getTreeBuildingDefault() {
        return booleanValue("TREE_BUILDING_DEFAULT") && getTreeBuildingEnabled();
    }
    
    public boolean getTokensAreNodes() {
        return booleanValue("TOKENS_ARE_NODES");
    }
    
    public boolean getSpecialTokensAreNodes() {
        return booleanValue("SPECIAL_TOKENS_ARE_NODES");
    }
    
    public boolean getFreemarkerNodes() {
        return booleanValue("FREEMARKER_NODES");
    }
    
    public String getBaseSourceDirectory() {
        return stringValue("BASE_SRC_DIR");
    }
    
    
    /**
     * Some warnings if incompatible options are set.
     */
    public void sanityCheck() {
        boolean baseSourceDefined = getBaseSourceDirectory().length() >0;
        boolean nodePackageDefined = getNodePackage().length() >0;
        boolean nodeClassDefined = getNodeClass().length() > 0;
        boolean parserPackageDefined = getParserPackage().length() >0;
        if (!baseSourceDefined) {
            if (getNodePackage().length() > 0) {
                String s = "You have defined the NODE_PACKAGE option but no BASE_SRC_DIR."
                            + " The option will be ignored since it only makes sense relative "
                            + "to a specified base directory for source files.";
                grammar.addWarning(null, s);
            }
        }
        
        if (!getTreeBuildingEnabled()) {
            String msg = "You have specified the OPTION_NAME option but it is "
                + "meaningless unless you set TREE_BUILDING_ENABLED=true."
                + " This option will be ignored.\n";
            if (nodePackageDefined) {
                grammar.addWarning(null, msg.replace("OPTION_NAME", "NODE_PACKAGE"));
            }
            if (nodeClassDefined) {
                grammar.addWarning(null, msg.replace("OPTION_NAME", "NODE_CLASS"));
            }
            if (getTokensAreNodes()) {
                grammar.addWarning(null, msg.replace("OPTION_NAME", "TOKENS_ARE_NODES"));
            }
            if (getSpecialTokensAreNodes()) {
                grammar.addWarning(null, msg.replace("OPTION_NAME", "SPECIAL_TOKENS_ARE_NODES"));
            }
            if (getSmartNodeCreation()) {
                grammar.addWarning(null, msg.replace("OPTION_NAME", "SMART_NODE_CREATION"));
            }
            if (getNodeDefaultVoid()) {
                grammar.addWarning(null, msg.replace("OPTION_NAME", "NODE_DEFAULT_VOID"));
            }
            if (getNodeScopeHook()) {
                grammar.addWarning(null, msg.replace("OPTION_NAME", "NODE_SCOPE_HOOK"));
            }
            if (getNodeUsesParser()) {
                grammar.addWarning(null, msg.replace("OPTION_NAME", "NODE_USES_PARSER"));
            }
            if (getVisitor()) {
                grammar.addWarning(null, msg.replace("OPTION_NAME", "VISITOR"));
            }
        }
    }
}
